#ifndef GEN_UTIL__H
#define GEN_UTIL__H

#include "structures/primitives.h"

typedef struct infos_fun_s {
	int frame;
    int labelE;
    int labelS;
    int labelEndIf;
    int nbParams;
    int registrePremierParam;
} infos_fun;

int frame = 0;
int label = 0;
int labelS = 0;
int registre = 0;

void gen_DFS(node* a, FILE* f, symtable *TS_func, symtable *TS_var)
{
	if (a != NULL)
	{
		preTraitement(a,f,TS_func,TS_var);
		gen_DFS(a->enfants,f,TS_func,TS_var);
		traitement(a,f,TS_var);
		node* child = a->enfants;
		if (child != NULL)
		{
			while(child->frere != NULL)
			{
				interTraitement(a,f);
				gen_DFS(child->frere,f,TS_func,TS_var);
				intraTraitement(a,f);
				child = child->frere;
			}
		}
		postTraitement(a,f);
	}
}

void preTraitement(node* a, FILE* f,symtable* TS_func, symtable* TS_var)
{
	switch(a->type)
		{
			case TMAIN :
			{
				fprintf(f,"\nMain:\n");
				break;
			}
			case TPROCEDURE:
			case TFUNCTION :
			{
				createFrame(a,f,TS_func,TS_var);
				break;
			}
			case TIF : 
			{
				infos_fun* s = (infos_fun*)malloc(sizeof(infos_fun));
				s->labelE = label;
				s->labelS = label+1;
				s->labelEndIf = label+2;
				a->infos = (void*)s;
				label += 3;
				fprintf(f, "jump(");
				break;
			}
			case TWHILE :
			{
				infos_fun* s = (infos_fun*)malloc(sizeof(infos_fun));
				s->labelE = label;
				a->infos = s;
				fprintf(f, "L%i :\njump(",label);
				label++;
				break;
			}
			case TRETURN :
			{
				if(a->enfants)
					fprintf(f,"$v0:=");
				break;
				
			}
			case TCALLFUN :
			{
				entree* e = getEntry(a->lexeme,TS_func);
				int registreFun = getRegister(e);
				fprintf(f, "call fr%i(",registreFun);
				break;
			}
			case TVAR :
			{
				break;
			}
		}
}

void traitement(node* a, FILE* f,symtable* TS_var)
{
	switch(a->type)
		{
			case TIF : 
			{
				infos_fun* infos = a->infos;
				fprintf(f, ")L%i,L%i\n",infos->labelE,infos->labelS);
				//label += 2;
				break;
			}
			case TWHILE :
			{
				infos_fun* infos = a->infos;
				infos->labelS = label+1;
				fprintf(f, "),L%i,L%i\nL%i:\n\n",label,label+1,label);
				label += 2;
				break;
			}
			case TRETURN :
			{
				fprintf(f,"\ngoto L%i\n",labelS);
				fprintf(f,"\nL%i:\nNoP\n",label);
				label++;
				break;
			}
			case TOPBINAIRE :
			{
				fprintf(f, "%s",a->lexeme);
				break;
			}
			case TVAR :
			{
				//int registre = *(int*)(a->infos);
				entree* e = getEntry(a->lexeme,TS_var);
				int registreVar = (int)a->infos;
                fprintf(f, "$%i",registreVar);
				break;
			}
			case TCST :
			{
				fprintf(f, "%s",a->lexeme);
				break;
			}
		}
}

void interTraitement(node* a, FILE* f)
{
	switch(a->type)
		{
			case TIF :
			{
				infos_fun* infos = a->infos;
				if (infos->labelE != NULL)
				{
					fprintf(f, "\nL%i: \n",infos->labelE);
					infos->labelE = NULL;
				}
				else
				{
					fprintf(f, "\nL%i: \n",infos->labelS);
					infos->labelS = NULL;
				}
				break;
			}
			case TWHILE :
			{
				//infos_fun* infos = a->infos;
				//fprintf(f, "goto L%i\n",infos->labelE);
				break;
			}
			case TRETURN :
			{
				//fprintf()
				break;
			}
			case TCALLFUN : 
			{
				fprintf(f,",");
				break;
			}
		}
}

void intraTraitement(node* a, FILE* f)
{
	switch(a->type)
	{		
		case TWHILE :
		{
			infos_fun* infos = a->infos;
			fprintf(f, "\ngoto L%i\n",infos->labelE);
			break;
		}
		case TIF :
		{
			infos_fun* infos = a->infos;
			fprintf(f,"goto L%i\n",infos->labelEndIf);
			break;
		}
	}
}

void postTraitement(node* a, FILE* f)
{
	switch(a->type)
		{
			case TPROCEDURE:
			case TFUNCTION :
			{
				infos_fun* infos;
				infos = a->infos;
				fprintf(f,"\nL%i:\n",infos->labelS);
				label++;
				break;
			}
			case TIF : 
			{
				infos_fun* infos = a->infos;
				fprintf(f,"\nL%i:\nNoP \n",infos->labelEndIf);
				break;
			}
			case TWHILE :
			{
				infos_fun* infos = a->infos;
				fprintf(f,"L%i :\nNoP\n",infos->labelS);
				break;
			}
			case TRETURN :
			{
				break;
			}
			case TCALLFUN :
			{
				fprintf(f,")\n");
				break;
			}
			case TOPBINAIRE :
			{
				char* s = a->lexeme;
				if(strcmp(":=",s)==0)
					fprintf(f,"\n");
			}
		}
}

void createFrame(node* a, FILE* f, symtable *TS_func, symtable *TS_var)
{
	entree* eFun = getEntry(a->lexeme,TS_func);
	int registreFun = getRegister(eFun);
	fprintf(f, "fr%i : { entry = L%i; return = L%i; args = [",registreFun,label, label+1);
	
	infos_fun* s = (infos_fun*)malloc(sizeof(infos_fun));
	s->frame = frame;
	frame ++;
	s->labelE = label;
	s->labelS = label+1;
	labelS = s->labelS;
	label += 2;
	
	int nbParams = nbParameters(a, TS_func, TS_var);
	s->nbParams = nbParams;
	//printf("nb paramètres %i \n",nbParams );
	
	entree* e = getEntry(a->lexeme,TS_func);
	param* params = getParams(e);
	
	int i = 0;
	// pour chaque parametre
	for (i = 0; i< nbParams-1; i++)
	{
		char* lexeme = params->lexeme;
		entree* eVar = getEntry(lexeme,TS_var);
		//int registreVar = getRegister(eVar);
		int registreVar = params->registre;
		fprintf(f, "$%i,",registreVar);
		if (i == 0)
			s->registrePremierParam = registreVar;
		params = params->next;
		
	}
	// pour le dernier
	if (params != NULL)
	{
		char* lexeme = params->lexeme;
		entree* eVar = getEntry(lexeme,TS_var);
		//int registreVar = getRegister(eVar);
		int registreVar = params->registre;
		fprintf(f, "$%i]",registreVar);
	}
	else
		fprintf(f,"]");
	if (nbParams == 1)
		s->registrePremierParam = registre;
	registre++;
	
	fprintf(f, " result = $v0");
	registre++;
	fprintf(f, " stack = %i }\n", 0);
	fprintf(f, "\nL%i : \nNoP\n", s->labelE);
	a->infos = (void*)s;
}

int nbParameters(node* a, symtable *TS_func, symtable *TS_var)
{
	int cpt = 0;
	char* funId = a->lexeme;
	entree* entreeFunc = getEntry(funId,TS_func);
	
	// Si l'entree est null c'est que la fonction appellée n'existe pas
	if (entreeFunc != NULL)
	{
		// on récupere la liste des paramètres de la fonction appelée
		// et on les compte !
		param* params = getParams(entreeFunc);

		if (params != NULL)
		{
			while(params->next != NULL)
			{
				cpt++;					
				params = params->next;
			}
		cpt++;
		}
	}
	return cpt;
}


#endif
